home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / bin / munchlist (.txt) < prev    next >
Microsoft Windows Help File Content  |  1995-07-02  |  25KB  |  595 lines

  1. : Use /bin/sh
  2. # $Id: munchlist.X,v 1.51 1994/11/21 07:02:54 geoff Exp $
  3. # Copyright 1987, 1988, 1989, 1992, 1993, Geoff Kuenning, Granada Hills, CA
  4. # All rights reserved.
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions
  7. # are met:
  8. # 1. Redistributions of source code must retain the above copyright
  9. #    notice, this list of conditions and the following disclaimer.
  10. # 2. Redistributions in binary form must reproduce the above copyright
  11. #    notice, this list of conditions and the following disclaimer in the
  12. #    documentation and/or other materials provided with the distribution.
  13. # 3. All modifications to the source code must be clearly marked as
  14. #    such.  Binary redistributions based on modified source code
  15. #    must be clearly marked as modified versions in the documentation
  16. #    and/or other materials provided with the distribution.
  17. # 4. All advertising materials mentioning features or use of this software
  18. #    must display the following acknowledgment:
  19. #      This product includes software developed by Geoff Kuenning and
  20. #      other unpaid contributors.
  21. # 5. The name of Geoff Kuenning may not be used to endorse or promote
  22. #    products derived from this software without specific prior
  23. #    written permission.
  24. # THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
  25. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27. # ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
  28. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34. # SUCH DAMAGE.
  35. #    Given a list of words for ispell, generate a reduced list
  36. #    in which all possible affixes have been collapsed.  The reduced
  37. #    list will match the same list as the original.
  38. #    Usage:
  39. #    munchlist [-l lang] [-c lang] [-s hashfile] [-D] [-w chars] [-v] \
  40. #      [file] ...
  41. #    Options:
  42. #    -l lang    Specifies the language table to be used.  The default
  43. #        is "$LIBDIR/english.aff".
  44. #    -c lang    Specifies "conversion" language table.  If this option is
  45. #        given, the input file(s) will be assumed to be described by
  46. #        this table, rather than the table given in the -l option.
  47. #        This may be used to convert between incompatible language
  48. #        tables.  (When in doubt, use this option -- it doesn't
  49. #        hurt, and it may save you from creating a dictionary that has
  50. #        illegal words in it).  The default is no conversion.
  51. #    -T suff Specifies that the source word lists are in the format
  52. #        of a "suff"-suffixed file, rather than in the
  53. #        canonical form.  For example, "-T tex" specifies that
  54. #        string characters in the word lists are in TeX format.
  55. #        The string character conversions are taken from the language
  56. #        table specified by the "-l" switch.
  57. #    -s    Remove any words that are already covered by the
  58. #        dictionary in 'hashfile'.  The words will be removed
  59. #        only if all affixes are covered.  This option should not be
  60. #        specified when the main dictionary is being munched.
  61. #        'Hashfile' must have been created with the language
  62. #        table given in the -l option, but this is not checked.
  63. #    -D    Leave temporary files for debugging purposes
  64. #    -w    Passed on to ispell (specify chars that are part of a word)
  65. #        Unfortunately, special characters must be quoted twice
  66. #        rather than once when invoking this script.  Also, since
  67. #        buildhash doesn't accept this option, the final ispell -l
  68. #        step ignores it, making it somewhat less than useful.
  69. #    -v    Report progress to stderr.
  70. #    The given input files are merged, then processed by 'ispell -c'
  71. #    to generate possible affix lists;  these are then combined
  72. #    and reduced.  The final result is written to standard output.
  73. #    For portability to older systems, I have avoided getopt.
  74. #        Geoff Kuenning
  75. #        2/28/87
  76. # $Log: munchlist.X,v $
  77. # Revision 1.51  1994/11/21  07:02:54  geoff
  78. # Correctly quote the arguments to 'tr' when detecting systems with
  79. # unsigned sorts.  Be sure to provide a zero exit status on all systems,
  80. # even if MUNCHDEBUG is not set.
  81. # Revision 1.50  1994/10/25  05:46:05  geoff
  82. # Export values for LANG and LOCALE in an attempt to override some
  83. # stupidly-internationalized sort programs.
  84. # Revision 1.49  1994/10/04  03:51:30  geoff
  85. # Add the MUNCHMAIL feature.  If the MUNCHMAIL environment variable is
  86. # set to an email address, debugging information about the munchlist run
  87. # will automatically be collected and mailed to that address.
  88. # Revision 1.48  1994/05/17  06:32:06  geoff
  89. # Don't look for affix tables in LIBDIR if the name contains a slash
  90. # Revision 1.47  1994/04/27  02:50:48  geoff
  91. # Fix some cosmetic flaws in the verbose-mode messages.
  92. # Revision 1.46  1994/01/25  07:11:59  geoff
  93. # Get rid of all old RCS log lines in preparation for the 3.1 release.
  94. if [ "X$MUNCHMAIL" != X ]
  95.     exec 2> /tmp/munchlist.mail
  96.     echo "munchlist $*" 1>&2
  97.     set -vx
  98. LIBDIR=/usr/skunk/lib/ispell-3.1
  99. TDIR=${TMPDIR-/usr/tmp}
  100. TMP=${TDIR}/munch$$
  101. SORTTMP="-T ${TDIR}"            # !!SORTTMP!!
  102. if [ -r ./icombine ]
  103.     COMBINE=./icombine
  104.     COMBINE=icombine
  105. if [ -r ./ijoin ]
  106.     JOIN=./ijoin
  107.     JOIN=ijoin
  108. # The following is necessary so that some internationalized version of
  109. # sort(1) don't confuse things by sorting into a nonstandard order.
  110. LANG=C
  111. LOCALE=C
  112. export LANG LOCALE
  113. debug=no
  114. dictopt=
  115. langtabs=${LIBDIR}/english.aff
  116. convtabs=
  117. strip=no
  118. icflags=
  119. verbose=false
  120. # The following value of "wchars" is necessary to prevent ispell from
  121. # receiving a null argument if -w is not specified.  As long as "A" is
  122. # a member of the existing character set, ispell will ignore the argument.
  123. wchars=-wA
  124. while [ $# != 0 ]
  125.     case "$1" in
  126.         case "$2" in
  127.         */*)
  128.             langtabs=$2
  129.             ;;
  130.             if [ -r "$2" ]
  131.             then
  132.             langtabs="$2"
  133.             else
  134.             langtabs="${LIBDIR}/$2"
  135.             fi
  136.             ;;
  137.         esac
  138.         if [ ! -r "$langtabs" ]
  139.         then
  140.         echo "Can't open language table '$2'" 1>&2
  141.         exit 1
  142.         fi
  143.         shift
  144.         ;;
  145.         if [ -r "$2" ]
  146.         then
  147.         convtabs="$2"
  148.         elif [ -r "${LIBDIR}/$2" ]
  149.         then
  150.         convtabs="${LIBDIR}/$2"
  151.         else
  152.         echo "Can't open conversion language table '$2'" 1>&2
  153.         exit 1
  154.         fi
  155.         shift
  156.         ;;
  157.         dictopt="-d $2"
  158.         strip=yes
  159.         shift
  160.         ;;
  161.         debug=yes
  162.         ;;
  163.         icflags="-T $2"
  164.         shift
  165.         ;;
  166.         verbose=true
  167.         ;;
  168.         wchars="-w$2"
  169.         shift
  170.         ;;
  171.         shift
  172.         break
  173.         ;;
  174.         break
  175.         ;;
  176.         echo 'Usage: munchlist [-l lang] [-c lang] [-T suff] [-s hashfile] [-D] [-w chars] [-v] [file] ...' \
  177.           1>&2
  178.         exit 2
  179.         ;;
  180.         break
  181.         ;;
  182.     esac
  183.     shift
  184. if [ "X$MUNCHMAIL" != X ]
  185.     verbose=true
  186.     debug=yes
  187. trap "/bin/rm -f ${TMP}*; exit 1" 1 2 13 15
  188. # Names of temporary files.  This is just to make the code a little easier
  189. # to read.
  190. EXPANDEDINPUT=${TMP}a
  191. STRIPPEDINPUT=${TMP}b
  192. CRUNCHEDINPUT=${TMP}c
  193. PRODUCTLIST=${TMP}d
  194. EXPANDEDPAIRS=${TMP}e
  195. LEGALFLAGLIST=${TMP}f
  196. JOINEDPAIRS=${TMP}g
  197. MINIMALAFFIXES=${TMP}h
  198. CROSSROOTS=${TMP}i
  199. CROSSEXPANDED=${TMP}j
  200. CROSSPAIRS=${TMP}k
  201. CROSSILLEGAL=${TMP}l
  202. ILLEGALCOMBOS=${TMP}m
  203. FAKEDICT=${TMP}n
  204. # Ispell insists that hash files have a ".hash" suffix
  205. FAKEHASH=${TMP}o.hash
  206. AWKSCRIPT=${TMP}p
  207. if [ "$debug" = yes ]
  208.     touch $EXPANDEDINPUT $STRIPPEDINPUT $CRUNCHEDINPUT $PRODUCTLIST \
  209.       $EXPANDEDPAIRS $LEGALFLAGLIST $JOINEDPAIRS $MINIMALAFFIXES \
  210.       $CROSSROOTS $CROSSEXPANDED $CROSSPAIRS $CROSSILLEGAL $ILLEGALCOMBOS \
  211.       $FAKEDICT $FAKEHASH $AWKSCRIPT
  212.     rm -f ${TDIR}/EXPANDEDINPUT ${TDIR}/STRIPPEDINPUT ${TDIR}/CRUNCHEDINPUT \
  213.       ${TDIR}/PRODUCTLIST ${TDIR}/EXPANDEDPAIRS ${TDIR}/LEGALFLAGLIST \
  214.       ${TDIR}/JOINEDPAIRS ${TDIR}/MINIMALAFFIXES ${TDIR}/CROSSROOTS \
  215.       ${TDIR}/CROSSEXPANDED ${TDIR}/CROSSPAIRS ${TDIR}/CROSSILLEGAL \
  216.       ${TDIR}/ILLEGALCOMBOS ${TDIR}/FAKEDICT ${TDIR}/FAKEHASH.hash \
  217.       ${TDIR}/AWKSCRIPT ${TDIR}/CROSSROOTS.[0-9]* ${TDIR}/CROSSEXP.[0-9]* \
  218.       ${TDIR}/CROSSPAIRS.[0-9]* ${TDIR}/CROSSILLEGAL.[0-9]*
  219.     ln $EXPANDEDINPUT ${TDIR}/EXPANDEDINPUT
  220.     ln $STRIPPEDINPUT ${TDIR}/STRIPPEDINPUT
  221.     ln $CRUNCHEDINPUT ${TDIR}/CRUNCHEDINPUT
  222.     ln $PRODUCTLIST ${TDIR}/PRODUCTLIST
  223.     ln $EXPANDEDPAIRS ${TDIR}/EXPANDEDPAIRS
  224.     ln $LEGALFLAGLIST ${TDIR}/LEGALFLAGLIST
  225.     ln $JOINEDPAIRS ${TDIR}/JOINEDPAIRS
  226.     ln $MINIMALAFFIXES ${TDIR}/MINIMALAFFIXES
  227.     ln $CROSSROOTS ${TDIR}/CROSSROOTS
  228.     ln $CROSSEXPANDED ${TDIR}/CROSSEXPANDED
  229.     ln $CROSSPAIRS ${TDIR}/CROSSPAIRS
  230.     ln $CROSSILLEGAL ${TDIR}/CROSSILLEGAL
  231.     ln $ILLEGALCOMBOS ${TDIR}/ILLEGALCOMBOS
  232.     ln $FAKEDICT ${TDIR}/FAKEDICT
  233.     ln $FAKEHASH ${TDIR}/FAKEHASH.hash
  234.     ln $AWKSCRIPT ${TDIR}/AWKSCRIPT
  235. # Create a dummy dictionary to hold a compiled copy of the language
  236. # table.  Initially, it holds the conversion table, if it exists.
  237. case "X$convtabs" in
  238.     X)
  239.     convtabs="$langtabs"
  240. echo 'QQQQQQQQ' > $FAKEDICT
  241. buildhash -s $FAKEDICT $convtabs $FAKEHASH \
  242.   ||  (echo "Couldn't create fake hash file" 1>&2; /bin/rm -f ${TMP}*; exit 1) \
  243.   ||  exit 1
  244. # Figure out how 'sort' sorts signed fields, for arguments to ijoin.
  245. # This is a little bit of a tricky pipe, but the result is that SIGNED
  246. # is set to "-s" if characters with the top bit set sort before those
  247. # without, and "-u" if the reverse is true.  How does it work?  The
  248. # first "tr" step generates two lines, one containing "-u", the other
  249. # with the same but with the high-order bit set.  The second "tr"
  250. # changesthe high-bit "-u" back to "-s".  If the high-bit "-u" was
  251. # sorted first, the sed step will select "-s" for SIGNED; otherwise
  252. # it'll pick "-u".
  253. SIGNED=`echo '-s
  254. -u' | tr s \\\\365 | sort | tr \\\\365 s | sed 1q`
  255. # Collect all the input and expand all the affix options (ispell -e),
  256. # and preserve (sorted) for later joining in EXPANDEDINPUT.  The icombine
  257. # step is to make sure that unneeded capitalizations (e.g., Farmer and farmer)
  258. # are weeded out.  The first sort must be folded for icombine;  the second
  259. # must be unfolded for join.
  260. $verbose  &&  echo "Collecting input." 1>&2
  261. if [ $# -eq 0 ]
  262.     ispell "$wchars" -e1 -d $FAKEHASH -p /dev/null | tr " " '
  263.     cat "$@" | ispell "$wchars" -e1 -d $FAKEHASH -p /dev/null | tr " " '
  264.   | sort $SORTTMP -u +0f -1 +0 \
  265.   | $COMBINE $icflags $langtabs \
  266.   | sort $SORTTMP -u > $EXPANDEDINPUT
  267. # If a conversion table existed, recreate the fake hash file with the
  268. # "real" language table.
  269. case "$convtabs" in
  270.     $langtabs)
  271.     *)
  272.     buildhash -s $FAKEDICT $langtabs $FAKEHASH \
  273.       ||  (echo "Couldn't create fake hash file" 1>&2; \
  274.         /bin/rm -f ${TMP}*; exit 1) \
  275.       ||  exit 1
  276. /bin/rm -f ${FAKEDICT}*
  277. # If the -s (strip) option was specified, remove all
  278. # expanded words that are covered by the dictionary.  This produces
  279. # the final list of expanded words that this dictionary must cover.
  280. # Leave the list in STRIPPEDINPUT.
  281. if [ "X$strip" = "Xno" ]
  282.     rm -f $STRIPPEDINPUT
  283.     ln $EXPANDEDINPUT $STRIPPEDINPUT
  284.     if [ "$debug" = yes ]
  285.     then
  286.     rm -f ${TDIR}/STRIPPEDINPUT
  287.     ln $STRIPPEDINPUT ${TDIR}/STRIPPEDINPUT
  288.     fi
  289.     $verbose  &&  echo "Stripping words already in the dictionary." 1>&2
  290.     ispell "$wchars" -l $dictopt -p /dev/null < $EXPANDEDINPUT \
  291.       > $STRIPPEDINPUT
  292. # Figure out what the flag-marking character is.
  293. $verbose  &&  echo "Finding flag marker." 1>&2
  294. flagmarker=`ispell -D -d $FAKEHASH \
  295.   | sed -n '/^flagmarker/s/flagmarker //p'`
  296. case "$flagmarker" in
  297.     \\*)
  298.     flagmarker=`expr "$flagmarker" : '.\(.\)'`
  299. esac    
  300. # Munch the input to generate roots and affixes (ispell -c).  We are
  301. # only interested in words that have at least one affix (egrep $flagmarker);
  302. # the next step will pick up the rest.  Some of the roots are illegal.  We
  303. # use join to restrict the output to those root words that are found
  304. # in the original dictionary.
  305. $verbose  &&  echo "Generating roots and affixes." 1>&2
  306. ispell "$wchars" -c -W0 -d $FAKEHASH -p /dev/null < $STRIPPEDINPUT \
  307.   | tr " " '
  308.   | egrep "$flagmarker" | sort $SORTTMP -u "-t$flagmarker" +0 -1 +1 \
  309.   | $JOIN $SIGNED "-t$flagmarker" - $EXPANDEDINPUT > $CRUNCHEDINPUT
  310. # We now have a list of legal roots, and of affixes that apply to the
  311. # root words.  However, it is possible for some affix flags to generate more
  312. # than one output word.  For example, with the flag table entry
  313. #    flag R:    . > ER
  314. #        . > ERS
  315. # the input "BOTHER" will generate an entry "BOTH/R" in CRUNCHEDINPUT.  But
  316. # this will accept "BOTHER" and "BOTHERS" in the dictionary, which is
  317. # wrong (in this case, though it's good English).
  318. # To cure this problem, we first have to know which flags generate which
  319. # expansions.  We use ispell -e3 to expand the flags (the second e causes
  320. # the root and flag to be included in the output), and get pairs
  321. # suitable for joining.  In the example above, we would get
  322. #    BOTH/R BOTHER
  323. #    BOTH/R BOTHERS
  324. # We save this in EXPANDEDPAIRS for the next step.
  325. $verbose  &&  echo 'Expanding dictionary into EXPANDEDPAIRS.' 1>&2
  326. ispell "$wchars" -e3 -d $FAKEHASH -p /dev/null < $CRUNCHEDINPUT \
  327.   | sort $SORTTMP +1 > $EXPANDEDPAIRS
  328. # Now we want to extract the lines in EXPANDEDPAIRS in which the second field
  329. # is *not* listed in the original dictionary EXPANDEDINPUT;  these illegal
  330. # lines contain the flags we cannot include without accepting illegal words.
  331. # It is somewhat easier to extract those which actually are listed (with
  332. # join), and then use comm to strip these from EXPANDEDPAIRS to get the
  333. # illegal expansions, together with the flags that generate them (we must
  334. # re-sort EXPANDEDPAIRS before running comm).  Sed
  335. # gets rid of the expansion and uniq gets rid of duplicates.  Comm then
  336. # selects the remainder of the list from CRUNCHEDINPUT and puts it in
  337. # LEGALFLAGLIST.  The final step is to use a sort and icombine to put
  338. # the list into a one-entry-per-root format.
  339. # BTW, I thought of using cut for the sed step (on systems that have it),
  340. # but it turns out that sed is faster!
  341. $JOIN -j1 2 -o 1.1 1.2 $SIGNED $EXPANDEDPAIRS $EXPANDEDINPUT \
  342.   | sort $SORTTMP -u > $JOINEDPAIRS
  343. sort $SORTTMP -o $EXPANDEDPAIRS $EXPANDEDPAIRS
  344. sort $SORTTMP -o $CRUNCHEDINPUT $CRUNCHEDINPUT
  345. $verbose  &&  echo 'Creating list of legal roots/flags.' 1>&2
  346. comm -13 $JOINEDPAIRS $EXPANDEDPAIRS \
  347.   | (sed -e 's; .*$;;' ; /bin/rm -f $JOINEDPAIRS $EXPANDEDPAIRS) \
  348.   | uniq \
  349.   | (comm -13 - $CRUNCHEDINPUT ; /bin/rm -f $CRUNCHEDINPUT) \
  350.   | sort $SORTTMP -u "-t$flagmarker" +0f -1 +0 \
  351.   | $COMBINE $langtabs > $LEGALFLAGLIST
  352. # LEGALFLAGLIST now contains root/flag combinations that, when expanded,
  353. # produce only words from EXPANDEDPAIRS.  However, there is still a
  354. # problem if the language tables have any cross-product flags.  A legal
  355. # root may appear in LEGALFLAGLIST with two flags that participate
  356. # in cross-products.  When such a dictionary entry is expanded,
  357. # the cross-products will generate some extra words that may not
  358. # be in EXPANDEDPAIRS.  We need to remove these from LEGALFLAGLIST.
  359. # The first step is to collect the names of the flags that participate
  360. # in cross-products.  Ispell will dump the language tables for us, and
  361. # sed is a pretty handy way to strip out extra information.  We use
  362. # uniq -c and a numerical sort to put the flags in approximate order of how
  363. # "productive" they are (in terms of how likely they are to generate a lot
  364. # of output words).  The least-productive flags are given last and will
  365. # be removed first.
  366. $verbose \
  367.   &&  echo 'Creating list of flags that participate in cross-products.' 1>&2
  368. ispell -D -d $FAKEHASH \
  369.   | sed -n '1,$s/:.*$//
  370.     /^flagmarker/d
  371.     /^prefixes/,/^suffixes/s/^  flag \*/p /p
  372.     /^suffixes/,$s/^  flag \*/s /p' \
  373.   | sort $SORTTMP \
  374.   | uniq -c \
  375.   | sort $SORTTMP +0rn -1 +2 > $PRODUCTLIST
  376. if [ `egrep ' p ' $PRODUCTLIST | wc -l` -gt 0 \
  377.   -a `egrep ' s ' $PRODUCTLIST | wc -l` -gt 0 ]
  378.     #
  379.     # The language tables allow cross products.  See if LEGALFLAGLIST has
  380.     # any roots with multiple cross-product flags.  Put them in CROSSROOTS.
  381.     #
  382.     $verbose  &&  echo 'Finding prefix and suffix flags.' 1>&2
  383.     preflags=`sed -n 's/^[ 0-9]*p //p' $PRODUCTLIST | tr -d '
  384.     sufflags=`sed -n 's/^[ 0-9]*s //p' $PRODUCTLIST | tr -d '
  385.     egrep "$flagmarker.*[$preflags].*[$sufflags]|$flagmarker.*[$sufflags].*[$preflags]" \
  386.       $LEGALFLAGLIST \
  387.       > $CROSSROOTS
  388.     #
  389.     # We will need an awk script;  it's so big that it core-dumps my shell
  390.     # under certain conditions.  The rationale behind the script is commented
  391.     # where the script is used.  Note that you may want to change this
  392.     # script for languages other than English.
  393.     #
  394.     case "$flagmarker" in
  395.         sedchar=:
  396.         ;;
  397.         sedchar=/
  398.         ;;
  399.     esac
  400.     $verbose  &&  echo 'Creating awk script.' 1>&2
  401.     sed -e "s/PREFLAGS/$preflags/" -e "s/SUFFLAGS/$sufflags/" \
  402.       -e "s;ILLEGALCOMBOS;$ILLEGALCOMBOS;" \
  403.       -e "s${sedchar}FLAGMARKER${sedchar}$flagmarker${sedchar}" \
  404.       > $AWKSCRIPT << 'ENDOFAWKSCRIPT'
  405.     BEGIN \
  406.         {
  407.         preflags = "PREFLAGS"
  408.         sufflags = "SUFFLAGS"
  409.         illegalcombos = "ILLEGALCOMBOS"
  410.         flagmarker = "FLAGMARKER"
  411.         pflaglen = length (preflags)
  412.         for (i = 1;  i <= pflaglen;  i++)
  413.         pflags[i] = substr (preflags, i, 1);
  414.         sflaglen = length (sufflags)
  415.         for (i = 1;  i <= sflaglen;  i++)
  416.         sflags[i] = substr (sufflags, i, 1);
  417.         }
  418.         {
  419.         len = length ($2)
  420.         pnew2 = ""
  421.         snew2 = ""
  422.         pbad = ""
  423.         sbad = ""
  424.         sufs = 0
  425.         pres = 0
  426.         for (i = 1;  i <= len;  i++)
  427.         curflag = substr ($2, i, 1)
  428.         for (j = 1;  j <= pflaglen;  j++)
  429.             {
  430.             if (pflags[j] == curflag)
  431.             pres++
  432.             pnew2 = substr ($2, 1, i - 1) substr ($2, i + 1)
  433.             pbad = curflag
  434.             }
  435.         for (j = 1;  j <= sflaglen;  j++)
  436.             {
  437.             if (sflags[j] == curflag)
  438.             sufs++
  439.             snew2 = substr ($2, 1, i - 1) substr ($2, i + 1)
  440.             sbad = curflag
  441.             }
  442.         if (pres == 1)
  443.         print $1 flagmarker pnew2
  444.         print $1 flagmarker pbad >> illegalcombos
  445.         else if (sufs == 1)
  446.         print $1 flagmarker snew2
  447.         print $1 flagmarker sbad >> illegalcombos
  448.         else if (pres > 0)
  449.         print $1 flagmarker pnew2
  450.         print $1 flagmarker pbad >> illegalcombos
  451.         else
  452.         print $1 flagmarker snew2
  453.         print $1 flagmarker sbad >> illegalcombos
  454.         }
  455. ENDOFAWKSCRIPT
  456.     : > $ILLEGALCOMBOS
  457.     dbnum=0
  458.     while [ -s $CROSSROOTS ]
  459.     do
  460.     # CROSSROOTS contains the roots whose cross-product expansions
  461.     # might be illegal.  We now need to locate the actual illegal ones.
  462.     # We do this in much the same way we created LEGALFLAGLIST from
  463.     # CRUNCHEDINPUT.  First we make CROSSEXPANDED, which is analogous
  464.     # to EXPANDEDPAIRS.
  465.     $verbose  &&  echo "Creating cross expansions (pass $dbnum)." 1>&2
  466.     ispell "$wchars" -e3 -d $FAKEHASH -p /dev/null < $CROSSROOTS \
  467.       | sort $SORTTMP +1 > $CROSSEXPANDED
  468.     # Now we join CROSSEXPANDED against EXPANDEDINPUT to produce
  469.     # CROSSPAIRS, and then comm that against CROSSEXPANDED to
  470.     # get CROSSILLEGAL, the list of illegal cross-product flag
  471.     # combinations.
  472.     $JOIN -j1 2 -o 1.1 1.2 $SIGNED $CROSSEXPANDED $EXPANDEDINPUT \
  473.       | sort $SORTTMP -u > $CROSSPAIRS
  474.     sort $SORTTMP -u -o $CROSSEXPANDED $CROSSEXPANDED
  475.     $verbose \
  476.       &&  echo "Finding illegal cross expansions (pass $dbnum)." 1>&2
  477.     comm -13 $CROSSPAIRS $CROSSEXPANDED \
  478.       | sed -e 's; .*$;;' \
  479.       | uniq > $CROSSILLEGAL
  480.     if [ "$debug" = yes ]
  481.     then
  482.         mv $CROSSROOTS $TDIR/CROSSROOTS.$dbnum
  483.         ln $CROSSEXPANDED $TDIR/CROSSEXP.$dbnum
  484.         ln $CROSSPAIRS $TDIR/CROSSPAIRS.$dbnum
  485.         ln $CROSSILLEGAL $TDIR/CROSSILLEGAL.$dbnum
  486.     # Now it is time to try to clear up the illegalities.  For 
  487.     # each word in the illegal list, remove one of the cross-product
  488.     # flags.  The flag chosen is selected in an attempt to cure the
  489.     # problem quickly, as follows:  (1) if there is only one suffix
  490.     # flag or only one prefix flag, we remove that.  (2) If there is
  491.     # a prefix flag, we remove the "least desirable" (according to
  492.     # the order of preflags). (This may be pro-English prejudice,
  493.     # and you might want to change this if your language is prefix-heavy).
  494.     # (3) Otherwise we remove the least-desirable suffix flag
  495.     # The output of the awk script becomes the new CROSSROOTS.  In
  496.     # addition, we add the rejected flags to ILLEGALCOMBOS (this is done
  497.     # inside the awk script) so they can be removed from LEGALFLAGLIST
  498.     # later.
  499.     awk "-F$flagmarker" -f $AWKSCRIPT $CROSSILLEGAL > $CROSSROOTS
  500.     if [ "$debug" = yes ]
  501.     then
  502.         /bin/rm -f $CROSSEXPANDED $CROSSPAIRS $CROSSILLEGAL
  503.     dbnum=`expr $dbnum + 1`
  504.     done
  505.     /bin/rm -f $CROSSEXPANDED $CROSSPAIRS $CROSSILLEGAL $AWKSCRIPT
  506.     #
  507.     # Now we have, in ILLEGALCOMBOS, a list of root/flag combinations
  508.     # that must be removed from LEGALFLAGLIST to get the final list
  509.     # of truly legal flags.  ILLEGALCOMBOS has one flag per line, so
  510.     # by turning LEGALFLAGLIST into this form (sed), it's an
  511.     # easy task for comm.  We have to recombine flags again after the
  512.     # extraction, to get all flags for a given root on the same line so that
  513.     # cross-products will come out right.
  514.     #
  515.     if [ -s $ILLEGALCOMBOS ]
  516.     then
  517.     sort $SORTTMP -u -o $ILLEGALCOMBOS $ILLEGALCOMBOS
  518.     $verbose  &&  echo 'Finding roots of cross expansions.' 1>&2
  519.     sort $SORTTMP $LEGALFLAGLIST \
  520.       | sed '/\/../{
  521.           s;^\(.*\)/\(.\)\(.*\);\1/\2\
  522. \1/\3;
  523.           P
  524.           D
  525.           }' \
  526.       | comm -23 - $ILLEGALCOMBOS \
  527.       | sort $SORTTMP -u "-t$flagmarker" +0f -1 +0 \
  528.       | $COMBINE $langtabs > $CROSSROOTS
  529.     mv $CROSSROOTS $LEGALFLAGLIST
  530.     if [ "$debug" = yes ]
  531.     then
  532.         rm -f ${TDIR}/LEGALFLAGLIST1
  533.         ln $LEGALFLAGLIST ${TDIR}/LEGALFLAGLIST1
  534.     fi
  535. /bin/rm -f $PRODUCTLIST $CROSSROOTS $ILLEGALCOMBOS $EXPANDEDINPUT
  536. # We now have (in LEGALFLAGLIST) a list of roots and flags which will
  537. # accept words taken from EXPANDEDINPUT and no others (though some of
  538. # EXPANDEDINPUT is not covered by this list).  However, many of the
  539. # expanded words can be generated in more than one way.  For example,
  540. # "bather" can be generated from "bath/R" and "bathe/R".  This wastes
  541. # unnecessary space in the raw dictionary and, in some cases, in the
  542. # hash file as well.  The solution is to list the various ways of
  543. # getting a given word and choose exactly one.  All other things being
  544. # equal, we want to choose the one with the highest expansion length
  545. # to root length ratio.  The ispell -e4 option takes care of this by
  546. # providing us with a field to sort on.
  547. # The ispell/awk combination is similar to the ispell/sed pipe used to
  548. # generate EXPANDEDPAIRS, except that ispell adds an extra field
  549. # giving the sort order.  The first sort gets things in order so the
  550. # first root listed is the one we want, and the second sort (-um) then
  551. # selects that first root.  Sed strips the expansion from the root,
  552. # and a final sort -u generates MINIMALAFFIXES, the final list of
  553. # affixes that (more or less) minimally covers what it can from
  554. # EXPANDEDINPUT.
  555. $verbose  &&  echo 'Eliminating non-optimal affixes.' 1>&2
  556. ispell "$wchars" -e4 -d $FAKEHASH -p /dev/null < $LEGALFLAGLIST \
  557.   | sort $SORTTMP +1 -2 +2rn -3 +0 -1 \
  558.   | sort $SORTTMP -um +1 -2 \
  559.   | sed -e 's; .*$;;' \
  560.   | sort $SORTTMP -u "-t$flagmarker" +0f -1 +0 > $MINIMALAFFIXES
  561. /bin/rm -f $LEGALFLAGLIST
  562. # Now we're almost done.  MINIMALAFFIXES covers some (with luck, most)
  563. # of the words in STRIPPEDINPUT.  Now we must create a list of the remaining
  564. # words (those omitted by MINIMALAFFIXES) and add it to MINIMALAFFIXES.
  565. # The best way to do this is to actually build a partial dictionary from
  566. # MINIMALAFFIXES in FAKEHASH, and then use ispell -l to list the words that
  567. # are not covered by this dictionary.  This must then be combined with the
  568. # reduced version of MINIMALAFFIXES and sorted to produce the final result.
  569. $verbose  &&  echo "Generating output word list." 1>&2
  570. if [ -s $MINIMALAFFIXES ]
  571.     buildhash -s $MINIMALAFFIXES $langtabs $FAKEHASH > /dev/null \
  572.       ||  (echo "Couldn't create intermediate hash file" 1>&2;
  573.     /bin/rm -f ${TMP}*;
  574.     exit 1) \
  575.       ||  exit 1
  576.     if [ "$debug" = yes ]
  577.     then
  578.     rm -f ${TDIR}/MINAFFIXES.cnt ${TDIR}/MINAFFIXES.stat
  579.     ln $MINIMALAFFIXES.cnt ${TDIR}/MINAFFIXES.cnt
  580.     ln $MINIMALAFFIXES.stat ${TDIR}/MINAFFIXES.stat
  581.     fi
  582.     (ispell "$wchars" -l -d $FAKEHASH -p /dev/null < $STRIPPEDINPUT; \
  583.     $COMBINE $langtabs < $MINIMALAFFIXES) \
  584.       | sort $SORTTMP "-t$flagmarker" -u +0f -1 +0
  585.     # MINIMALAFFIXES is empty;  just produce a sorted version of STRIPPEDINPUT
  586.     sort $SORTTMP "-t$flagmarker" -u +0f -1 +0 $STRIPPEDINPUT
  587. /bin/rm -f ${TMP}*
  588. if [ "X$MUNCHMAIL" != X ]
  589.     (
  590.     ls -ld ${TDIR}/[A-Z]*
  591.     cat /tmp/munchlist.mail
  592.     ) | mail "$MUNCHMAIL"
  593.     /bin/rm -f /tmp/munchlist.mail
  594. exit 0
  595.